/*
创建节点服务组件，用于处理游戏逻辑

	管理节点服务的生命周期（初始化、启动、停止）
	支持不同类型的节点（如：game、chat、match等）
	自动注册和注销到服务注册中心
	提供节点元数据信息（类型、状态、容量等）
	生成唯一的服务ID

2025/10/14
增加健康检查http服务

2025/10/16
去除rpc映射处理器，node有独立的http服务器
*/
package node

import (
	"context"
	"encoding/json"
	"fmt"
	"gameServer/log"
	"gameServer/registry"
	"gameServer/registry/memory"
	"gameServer/rpc"
	"math/rand"
	"net/http"
	"strconv"
	"time"

	"google.golang.org/grpc"
)

/* //* 节点服务组件实现，负责处理游戏逻辑，管理节点生命周期，提供RPC服务接口，支持健康检查和服务注册 */

// 节点组件
type Node struct {
	registry   registry.Registry                            // 注册中心，服务信息聚集位置
	serviceID  string                                       // 节点服务ID，唯一标识节点服务
	nodeType   string                                       // 节点类型，如：game、chat、match等
	address    string                                       // 节点服务地址
	port       int                                          // 节点服务端口
	rpcServer  *rpc.Server                                  // rpc 服务
	rpcPort    int                                          // 每一个节点都有自己的port，负责的rpc服务器只有一个端口
	healthPort int                                          // 健康检查端口
	grpcServer *grpc.Server                                 // gRPC服务实例
	httpServer *http.Server                                 // HTTP服务实例（承载健康检查、指标暴露功能）
	handlers   map[uint32]func(data []byte) ([]byte, error) // RPC处理器映射（key：路由ID，value：处理函数） # 弃用
}

// 创建节点组件
func NewNode(nodeType, address string, port int) *Node {
	// 生成随机服务ID
	rand.Seed(time.Now().UnixNano())
	serviceID := "node-" + nodeType + "-" + strconv.FormatInt(rand.Int63(), 10)
	// 健康检查端口使用节点端口 + 20000（过了10w会爆炸来着，真的没问题吗）
	healthPort := port + 20000

	// RPC 端口使用节点端口 + 10000
	rpcPort := port + 10000
	rpcServer := rpc.NewServer(rpcPort)
	node := &Node{
		registry:   memory.NewMemoryRegistry(),
		serviceID:  serviceID,
		nodeType:   nodeType,
		address:    address,
		port:       port,
		rpcServer:  rpcServer,
		rpcPort:    rpcPort,
		healthPort: healthPort,
		//# 弃用，node之间独立
		handlers: make(map[uint32]func(data []byte) ([]byte, error)),
	}

	// 创建http服务器用于健康检查（监听该端口所有ip）
	node.httpServer = &http.Server{
		Addr:    fmt.Sprintf(":%d", healthPort), // 监听地址：所有网卡的健康检查端口（如":28000"）
		Handler: node.createHealthHandler(),     // HTTP路由处理器（由createHealthHandler生成）
	}
	return node
}

// 创建健康检查处理器
func (n *Node) createHealthHandler() http.Handler {
	// 创建HTTP路由复用器（管理多个路由，如/health、/metrics）
	mux := http.NewServeMux()
	// 注册/health路由（健康检查核心接口，供健康检查组件调用）
	mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Write([]byte("OK")) // 响应体返回"OK"（简单文本，便于快速判断）
	})
	// 注册/metrics路由（暴露节点指标metrics就是指标），供监控系统采集）
	mux.HandleFunc("/metrics", func(w http.ResponseWriter, r *http.Request) {
		// 返回节点指标消息
		metrics := map[string]interface{}{
			"service_id": n.serviceID, // 节点唯一ID
			"node_type":  n.nodeType,  // 节点类型
			"status":     "healthy",   // 节点状态（当前硬编码为healthy，后续可改为动态状态）
			//"handlers":   len(n.handlers),   // 已注册的RPC处理器数量（反映节点业务负载）
			"timestamp": time.Now().Unix(), // 指标生成时间戳（秒级，便于监控时序分析）
		}
		// 将指标数据序列化为JSON并写入HTTP响应（设置响应头为application/json）
		json.NewEncoder(w).Encode(metrics)
	})
	// 返回路由复用器（作为http.Server的Handler）
	return mux
}

// 设置节点注册中心
func (n *Node) SetRegistry(registry registry.Registry) *Node {
	n.registry = registry
	return n
}

// 注册rpc处理器
func (n *Node) RegisterRPCHandler(route uint32, handler func(data []byte) ([]byte, error)) {
	n.rpcServer.RegisterHandler(route, handler)
}

// 初始化节点组件（有具体服务就可以来这个组件初始化）
func (n *Node) Init() error {
	// 初始化节点服务
	log.Infof("Node component (%s) is initializing...", n.nodeType)
	return nil
}

// 启动节点组件，http健康检查服务
func (n *Node) Start() error {
	log.Infof("Node component (%s) is starting...", n.nodeType)
	// 注册节点服务到注册中心
	serviceInfo := &registry.ServiceInfo{
		ID:      n.serviceID,
		Name:    "node",
		Address: n.address,
		Port:    n.port,
		Metadata: map[string]string{
			"type":       n.nodeType,                 // 节点类型
			"status":     "active",                   // 启动状态
			"capacity":   "100",                      // 假设容量（单个节点承载的业务上限）
			"rpcPort":    strconv.Itoa(n.rpcPort),    // 端口号转换成字符串类型
			"healthPort": strconv.Itoa(n.healthPort), // 添加健康检查端口
		},
	}
	if err := n.registry.Register(serviceInfo); err != nil {
		return err
	}
	log.Infof("Node service registered with ID: %s", n.serviceID)
	log.Infof("Node type: %s, Address: %s:%d, RPC: %d, Health: %d", n.nodeType, n.address, n.port, n.rpcPort, n.healthPort)
	// 启动rpc服务
	go func() {
		if err := n.rpcServer.Start(); err != nil {
			log.Errorf("RPC server failed to start: %v", err)
		}
	}()
	// 启动http健康检查服务器
	go func() {
		log.Infof("Health check server starting on port %d", n.healthPort)
		// 调用HTTP服务的ListenAndServe（阻塞监听端口，处理HTTP请求）
		if err := n.httpServer.ListenAndServe(); err != http.ErrServerClosed {
			log.Errorf("Health check server failed to start: %v", err)
		}
	}()
	return nil
}

// 停止节点组件,停止HTTP健康检查服务器
func (n *Node) Stop() error {
	log.Infof("Node component (%s) is stopping...", n.nodeType)
	// 停止rpc服务器
	if n.rpcServer != nil {
		if err := n.rpcServer.Stop(); err != nil {
			log.Errorf("RPC server failed to stop: %v", err)
		}
	}

	// 停止http服务器
	if n.httpServer != nil {
		// 创建带5秒超时的上下文（确保Shutdown不会无限阻塞）
		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel() // 函数退出时取消上下文，释放资源
		// 调用Shutdown优雅关闭HTTP服务（等待当前请求处理完成，再关闭端口）
		n.httpServer.Shutdown(ctx)
	}
	// 注销节点服务从注册中心
	if err := n.registry.Deregister(n.serviceID); err != nil {
		return err
	}
	// 这里不需要手动停止rpc服务，因为只要程序在，协程就不会停止
	// 只有ctrl + c才能停止整个运行
	log.Infof("Node service deregistered with ID: %s", n.serviceID)
	return nil
}

// 获取服务ID
func (n *Node) GetServiceID() string {
	return n.serviceID
}

// 获取节点类型
func (n *Node) GetNodeType() string {
	return n.nodeType
}

// 获取节点地址
func (n *Node) GetAddress() string {
	return n.address
}

// 获取节点端口
func (n *Node) GetPort() int {
	return n.port
}

// 获取rpc端口
func (n *Node) GetRPCPort() int {
	return n.rpcPort
}
